/*
 *
 */
package org.jiscinvolve.astrodabis.barberry.hibernate.core;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.Serializable;

import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GenerationType;
import javax.persistence.GeneratedValue;
import javax.persistence.UniqueConstraint;

import org.hibernate.annotations.Index;
import org.hibernate.annotations.NamedQuery;
import org.hibernate.annotations.NamedQueries;
import org.hibernate.annotations.GenericGenerator;

import org.springframework.stereotype.Repository;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;

import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; 

import org.jasypt.util.password.PasswordEncryptor;
import org.jasypt.util.password.BasicPasswordEncryptor ;
import org.jasypt.util.password.StrongPasswordEncryptor ;

import org.jiscinvolve.astrodabis.barberry.common.core.Tag;
import org.jiscinvolve.astrodabis.barberry.common.core.Job;
import org.jiscinvolve.astrodabis.barberry.common.core.Womble;
import org.jiscinvolve.astrodabis.barberry.common.core.Account;
import org.jiscinvolve.astrodabis.barberry.common.core.Identifier;
import org.jiscinvolve.astrodabis.barberry.common.core.SimpleIdent;
import org.jiscinvolve.astrodabis.barberry.common.core.AbstractIdent;
import org.jiscinvolve.astrodabis.barberry.common.core.exception.*;

import org.jiscinvolve.astrodabis.barberry.hibernate.annotation.*;

/**
 *
 */
@Entity
@Table(
    name = AccountEntity.DB_TABLE_NAME
    )
@NamedQueries(
        {
        @NamedQuery(
            name  = "account-all",
            query = "FROM AccountEntity"
            ),
        @NamedQuery(
            name  = "account-name",
            query = "FROM AccountEntity WHERE name = :name"
            )
        }
    )
public class AccountEntity
extends TrackedEntity
implements Account
    {

    /**
     * Our debug logger.
     * 
     */
    private static Log log = LogFactory.getLog(AccountEntity.class);

    public static final String DB_TABLE_NAME = "accounts" ;
    public static final String DB_IDENT_COL  = "ident" ;
    public static final String DB_NAME_COL   = "name"  ;
    public static final String DB_CRYPT_COL  = "crypt" ;

    @Repository
    public static class Factory
    extends AbstractFactory<Account, AccountEntity>
    implements Account.Factory
        {

        @Override
        public String clean(String string)
            {
            if (string != null)
                {
                return string.toLowerCase().replaceAll(
                    Account.NAME_EXCLUDE,
                    Account.NAME_REPLACE
                    );
                }
            else {
                return null ;
                }
            }

        @Override
        public Identifier ident(String ident)
            {
            try {
                return new SimpleIdent<Long>(
                    Long.valueOf(
                        ident
                        )
                    );
                }
            catch (NumberFormatException ouch)
                {
                throw new IdentFormatException(
                    ouch
                    );
                }
            }

        @Override
        @CreateObject
        public Account create(String name, String pass)
            {
            return createQuick(
                name,
                pass
                );
            }

        @Override
        @CreateQuick
        public Account createQuick(String name, String pass)
            {
            Account found = select(
                name
                );
            if (found != null)
                {
                throw new DuplicateEntryException() ;
                }
            else {
                return hibernate().insert(
                    new AccountEntity(
                        clean(
                            name
                            ),
                        pass
                        )
                    );
                }
            }

        @Override
        @SelectObject
        public Iterable<Account> select()
            {
            return hibernate().iterable(
                hibernate().query(
                    "account-all"
                    )
                );
            }

        @Override
        @SelectObject
        public Account select(Identifier ident)
            {
            return hibernate().select(
                AccountEntity.class,
                ident
                );
            }

        @Override
        @SelectObject
        public Account select(String name)
            {
            return hibernate().first(
                hibernate().query(
                    "account-name"
                    ).setString(
                        "name",
                        clean(
                            name
                            )
                        )
                );
            }
        }

    protected AccountEntity()
        {
        super();
        }        

    protected AccountEntity(String name, String pass)
        {
        super(true);
        this.name = name ;
        this.pass(
            pass
            );
        }

    @Id
    @Column(
        name = DB_IDENT_COL,
        unique = true,
        nullable = false,
        updatable = false
        )
    @GeneratedValue(
        generator="system-hilo"
        )
    @GenericGenerator(
        name="system-hilo",
        strategy="hilo"
        )
    protected Long ident ;

    @Override
    public Identifier ident()
        {
        return new AbstractIdent()
            {
            public Serializable value()
                {
                return ident ;
                }
            };
        }

    @Column(
        name = DB_NAME_COL,
        unique = true,
        nullable = false,
        updatable = false
        )
    private String name ;

    @Override
    public String name()
        {
        return this.name ;
        }

    @Column(
        name = DB_CRYPT_COL,
        unique = false,
        nullable = false,
        updatable = true
        )
    private String crypt ;

    @Override
    public void pass(String plain)
        {
        this.crypt = cryptor.encryptPassword(
            plain
            );
        }

    public boolean login(String plain)
        {
        return cryptor.checkPassword(
            plain,
            this.crypt
            );
        }

    /**
     * Our PasswordEncryptor.
     *
     */
    protected static PasswordEncryptor cryptor = new BasicPasswordEncryptor();


    @Override
    public boolean equals(Object that)
        {
        if (this == that)
            {
            return true ;
            }
        if (this.ident == null)
            {
            return false ;
            }
        if (that instanceof AccountEntity)
            {
            return this.ident.equals(
                ((AccountEntity)that).ident
                );
            }
        if (that instanceof Account)
            {
            return this.ident().equals(
                ((Account)that).ident()
                );
            }
        return false ;
        }

    @Override
    public Tags tags()
        {
        return new Tags()
            {
            @Override
            public Tag create(String name)
                {
                return Womble.womble().tags().create(
                    AccountEntity.this,
                    name
                    );
                }

            @Override
            public Tag createQuick(String name)
                {
                return Womble.womble().tags().createQuick(
                    AccountEntity.this,
                    name
                    );
                }

            @Override
            public Tag create(String name, String text)
                {
                return Womble.womble().tags().create(
                    AccountEntity.this,
                    name,
                    text
                    );
                }

            @Override
            public Tag createQuick(String name, String text)
                {
                return Womble.womble().tags().createQuick(
                    AccountEntity.this,
                    name,
                    text
                    );
                }

            @Override
            public Tag select(String name)
                {
                return Womble.womble().tags().select(
                    AccountEntity.this,
                    name
                    );
                }

            @Override
            public Iterable<Tag> select()
                {
                return Womble.womble().tags().select(
                    AccountEntity.this
                    );
                }

            @Override
            public Iterable<Tag> recent(int limit)
                {
                return Womble.womble().tags().recent(
                    AccountEntity.this,
                    limit
                    );
                }
            };
        }

    @Override
    public Jobs jobs()
        {
        return new Jobs()
            {

            @Override
            public Job create(Job.Bean bean)
                {
                return Womble.womble().jobs().create(
                    AccountEntity.this,
                    bean
                    );
                }

            @Override
            public Job createQuick(Job.Bean bean)
                {
                return Womble.womble().jobs().createQuick(
                    AccountEntity.this,
                    bean
                    );
                }

            @Override
            public Iterable<Job> select()
                {
                return Womble.womble().jobs().select(
                    AccountEntity.this
                    );
                }

            @Override
            public Iterable<Job> recent(int limit)
                {
                return Womble.womble().jobs().recent(
                    AccountEntity.this,
                    limit
                    );
                }

            };
        }

    @Override
    public String toString()
        {
        StringBuilder builder = new StringBuilder();
        builder.append(
            "Account["
            );
        builder.append(
            this.ident
            );
        builder.append(
            "]{ name{"
            );
        builder.append(
            this.name()
            );
        builder.append(
            "}}"
            );
        return builder.toString();
        }
    }

